package model.entities;

import helpers.Utils;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import model.dtos.DineroDTO;
import model.dtos.FacturaDTO;
import model.dtos.MesaDTO;
import model.exceptions.EntityInvalidaException;
import model.persistence.daos.FacturaDAO;
import model.persistence.daos.MesaDAO;
import model.persistence.daos.MozoDAO;
import model.persistence.daos.SectorDAO;

@Entity
@Table(name = "Mesas")
public class Mesa
{
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;

	@Column
	private int numero;

	@Column(name = "cantidadMaxComensales", nullable = false)
	private int cantidadMaxComensales;

	@Column(name = "cantidadActualComensales", nullable = false)
	private int cantidadActualComensales;

	@Column(name = "estaOcupada", nullable = false)
	private boolean estaOcupada;

	@ManyToOne
	@JoinColumn(name = "idMozoActual", nullable = true)
	private Mozo mozoActual;

	@ManyToOne(cascade = CascadeType.ALL)
	@JoinColumn(name = "idPedidoActual", nullable = true)
	private Pedido pedidoActual;

	// Doble relacion Sector-Mesa & Mesa-Sector (performance)
	@OneToOne
	@JoinColumn(name = "idSector", nullable = true)
	private Sector sector;

	public boolean abrir(int cantidadPersonas, int idMozo)
	{
		if (!this.estaOcupada())
		{
			Mozo mozo = MozoDAO.getById(idMozo);

			if (mozo != null && mozo.puedeAtender(this) && this.puedeAtender(cantidadPersonas))
			{
				this.estaOcupada = true;
				this.cantidadActualComensales = cantidadPersonas;
				this.setMozoActual(mozo);

				try
				{
					MesaDAO.save(this);
				}
				catch (EntityInvalidaException e)
				{
					return false;
				}

				return true;
			}
		}
		return false;
	}

	private boolean puedeAtender(int cantidadPersonas)
	{
		boolean estaReservada = estaReservada(cantidadPersonas);
		return !estaReservada && (this.cantidadMaxComensales >= cantidadPersonas);
	}

	private boolean estaReservada(int cantidadPersonas)
	{
		return !Reserva.chequearDisponibilidad(cantidadPersonas);
	}

	public boolean agregarItemCarta(int idItemCarta, int cantidad)
	{
		if (pedidoActual == null)
		{
			pedidoActual = new Pedido();
			pedidoActual.setMesa(this);
		}

		boolean success = this.pedidoActual.agregarItemCarta(idItemCarta, cantidad);
		if (success)
		{
			try
			{
				MesaDAO.save(this);
			}
			catch (EntityInvalidaException e)
			{
				success = false;
				e.printStackTrace();
			}
		}
		
		return success;
	}

	public void confirmarPedido()
	{
		pedidoActual.confirmar(this);
	}

	public FacturaDTO cerrar()
	{
		return this.generarFactura();
	}

	private FacturaDTO generarFactura()
	{
		try
		{
			Factura factura = new Factura();
			factura.setFecha(Utils.getFechaActual());
			factura.setMesa(this);
			factura.setMozo(this.mozoActual);
			factura.setMonto(this.calcularMontoFinal());

			FacturaDAO.save(factura);

			return factura.generarDTO();
		}
		catch (EntityInvalidaException e)
		{
			e.printStackTrace();
			return null;
		}
	}

	public float cobrar(DineroDTO pago)
	{
		float montoAbonado = Utils.calcularMonto(pago);
		float montoConsumido = this.calcularMontoFinal();

		return montoAbonado - montoConsumido;
	}

	private float calcularMontoFinal()
	{
		return pedidoActual != null ? pedidoActual.calcularMontoFinal() : 0;
	}

	public void limpiar()
	{
		this.cantidadActualComensales = 0;
		this.estaOcupada = false;
		this.mozoActual = null;
		this.pedidoActual = null;
	}

	// Getters & setters
	public int getId()
	{
		return id;
	}

	public void setId(int id)
	{
		this.id = id;
	}

	public int getNumero()
	{
		return numero;
	}

	public void setNumero(int numero)
	{
		this.numero = numero;
	}

	public boolean estaOcupada()
	{
		return estaOcupada;
	}

	public void setEstaOcupada(boolean estaOcupada)
	{
		this.estaOcupada = estaOcupada;
	}

	public int getCantidadMaxComensales()
	{
		return cantidadMaxComensales;
	}

	public void setCantidadMaxComensales(int cantidadMaxComensales)
	{
		this.cantidadMaxComensales = cantidadMaxComensales;
	}

	public int getCantidadActualComensales()
	{
		return cantidadActualComensales;
	}

	public void setCantidadActualComensales(int cantidadActualComensales)
	{
		this.cantidadActualComensales = cantidadActualComensales;
	}

	public Mozo getMozoActual()
	{
		return mozoActual;
	}

	public void setMozoActual(Mozo mozoActual)
	{
		this.mozoActual = mozoActual;
	}

	public Pedido getPedidoActual()
	{
		return pedidoActual;
	}

	public void setPedidoActual(Pedido pedidoActual)
	{
		this.pedidoActual = pedidoActual;
	}

	public Sector getSector()
	{
		return sector;
	}

	public void setSector(Sector sector)
	{
		this.sector = sector;
	}

	public static Mesa crearInstancia(MesaDTO _mesa)
	{
		Mesa mesa = new Mesa();
		mesa.setNumero(_mesa.getNumero());
		mesa.setId(_mesa.getId());
		mesa.setCantidadMaxComensales(_mesa.getCantidadMaxComensales());
		mesa.setSector((Sector) SectorDAO.getById(_mesa.getSector()));

		return mesa;
	}

	public MesaDTO generateDTO()
	{
		MesaDTO dto = new MesaDTO();
		if (this.sector != null)
		{
			dto.setId(this.id);
			dto.setCantidadMaxComensales(this.cantidadMaxComensales);
			dto.setNumero(this.numero);
			dto.setSector(this.sector.getId());
		}
		return dto;
	}
}
